#ifndef __THREAD_POOL_HPP__
#define __THREAD_POOL_HPP__
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <vector>
#include "log.hpp"
#include "LockGuard.hpp"
#include <queue>
#include "Thread.hpp"
#include <functional>
using namespace ThreadModule;
const int def_thread_sum = 3; // 默认线程数量

template <typename T>
class thread_pool
{
private:
    void Lock_queue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnLock_queue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void Wait_thread()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void Wake_up_thread()
    {
        pthread_cond_signal(&_cond);
    }
    void Wake_up_allthread()
    {
        pthread_cond_broadcast(&_cond);
    }
    thread_pool(int threadsum = def_thread_sum)
        : _threadsum(threadsum), _isruning(false), _waitsum(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    void HandlerTask(std::string name)// 类的成员方法，也可以成为另一个类的回调方法，方便我们继续类级别的互相调用！
    {
        LOG(INFO, "%s is running...\n", name.c_str());
        while (true)
        {
            // 1. 保证队列安全
            Lock_queue();
            // 当任务队列为空，线程运行时
            // 2. 队列中不一定有数据
            while (_Task_queue.empty() && _isruning) // 当任务队列为空，有线程在运行，每个被唤醒的线程都要重新判断条件
            {
                _waitsum++;    // 等待的线程数量加一
                Wait_thread(); // 将线程加入等待队列中，解锁--等待被唤醒--加锁
                _waitsum--;
            }
            // 2.1 如果线程池已经退出了 && 任务队列是空的
            if (_Task_queue.empty() && !_isruning)
            {
                UnLock_queue();
                break;
            }
            // 2.2 如果线程池不退出 && 任务队列不是空的
            // 2.3 如果线程池已经退出 && 任务队列不是空的 --- 处理完所有的任务，然后在退出
            // 3. 一定有任务, 处理任务
            // 一定有任务
            T t = _Task_queue.front(); // 取出任务
            _Task_queue.pop();
            UnLock_queue();
            LOG(DEBUG, "%s get a task\n", name.c_str());//打印日志
            // 4. 处理任务，这个任务属于线程独占的任务
            t();
            // 执行任务
            // LOG(DEBUG, "%s handler a task, result is: %s", name.c_str(), t.Result_tostring().c_str());
        }
    }
    void Init_thread_pool()
    {
        // 指向构建出所有的线程，并不启动
        for (int i = 0; i < _threadsum; i++)
        {
            std::string name = "thread-" + std::to_string(i + 1);
            _thread_pool.emplace_back(std::bind(&thread_pool::HandlerTask, this, std::placeholders::_1), name);
            LOG(INFO, "init thread %s done\n", name.c_str());//打印日志
        }
        _isruning = true;
    }
    void Start_threads()
    {
        for (auto &thread : _thread_pool)
        {
            thread.Start();
        }
    }
    ~thread_pool()
    {
        pthread_mutex_destroy(&_mutex);//释放互斥量
        pthread_cond_destroy(&_cond);//释放条件变量
    }

    thread_pool(const thread_pool<T>& ) = delete;
    thread_pool& operator()(const thread_pool<T>&) = delete;
public:
    static thread_pool<T> *GetInstance()
    {
        // 如果是多线程获取线程池对象下面的代码就有问题了！！
        // 只有第一次会创建对象，后续都是获取
        // 双判断的方式，可以有效减少获取单例的加锁成本，而且保证线程安全
        if (nullptr == _instance) // 保证第二次之后，所有线程，不用在加锁，直接返回_instance单例对象
        {
            LockGuard lockguard(_lock);
            if (nullptr == _instance)
            {
                _instance = new thread_pool<T>();
                _instance->Init_thread_pool();
                _instance->Start_threads();
                LOG(DEBUG, "创建线程池单例\n");
                return _instance;
            }
        }
        LOG(DEBUG, "获取线程池单例\n");
        return _instance;
    }
    bool Enqueue(T &in)
    {
        bool ret = false;
        if (_isruning)
        {
            Lock_queue();
            _Task_queue.push(in);
            if (_waitsum > 0) //等待队列中的线程数量大于0
            {
                Wake_up_thread();//唤醒等待队里中的一个线程
            }
            LOG(DEBUG, "enqueue task success\n");//打印日志
            ret = true;
            UnLock_queue();
        }
        return ret;
    }
    void Wait()
    {
        std::cout<<_waitsum<<std::endl;
        for (auto &thread : _thread_pool)
        {
            thread.Join();//等待所有线程
            LOG(INFO, "%s is quit...\n", thread.name().c_str());//打印日志
        }
    }
    void Stop()
    {
        Lock_queue();
        _isruning = false;
        Wake_up_allthread();
        UnLock_queue();
    }
private:
    int _threadsum; //线程数量
    std::vector<Thread> _thread_pool;//线程池结构
    std::queue<T> _Task_queue;//任务队列
    pthread_mutex_t _mutex;//互斥量
    pthread_cond_t _cond;//条件变量
    bool _isruning;//正在运行的队列
    int _waitsum;//正在等待的线程的数量


    //单例模式
    static thread_pool<T>* _instance;
    static pthread_mutex_t _lock;
};


template<typename T> 
thread_pool<T>* thread_pool<T>::_instance = nullptr;
template<typename T> 
pthread_mutex_t thread_pool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;
#endif